#include <types.h>
#include <sys_config.h>

#include <retcode.h>
#include <api/libc/string.h>
#include <api/libc/printf.h>
#include <osal/osal.h>
#include <hld/hld_dev.h>
#include <hld/dis/vpo.h>
#include <hld/decv/vdec_driver.h>
#include <hld/decv/decv.h>
#include <hld/ge/ge.h>
#include <hld/osd/osddrv.h>

#include <api/libimagedec/imagedec.h>
#include <api/libmp/png.h>
#include <api/libmp/pe.h>
#include "../../image_engine.h"
#include "../../plugin.h"
#include <api/libfs2/stdio.h>

//#define AD_PNG_DEBUG

#ifdef AD_PNG_DEBUG
#define AD_PNG_PRINTF	libc_printf
#else
#define AD_PNG_PRINTF(...)	do{}while(0)
#endif

#define ENABLE_PE_CACHE
#ifdef 	ENABLE_PE_CACHE
#define	PNG_BLOCK_SIZE		(320 * 1024)
#define PNG_CACHE_SIZE		(PNG_BLOCK_SIZE * 2)
#define PNG_BIT_SIZE		0x8000
#define PNG_DEC_SIZE		0x8e900
static int png_cache_id = -1;
static UINT8 *png_cache_buffer;
#endif

//extern int gif_cache_id;
//BOOL jpeg_decoder_task_not_over;        // added for bug fixing : avoid running of two decoder task at one time.
struct jpeg_file
{
	imagedec_id				id; 			//image id for decoder			
	Imagedec_Init_Config 	init_conf;		//configurefor decoder
	
	FILE					*fp;			//file pointer for JPEG
	unsigned long			task_terminated;	
	mp_callback_func		mp_cb;			//callback function for mp
	OSAL_ID					lock;
};

static struct jpeg_file jpeg_file;
static struct png_cfg png_cfg;
static struct image_display_t dpy;

static struct png_instance *png = NULL;

static int png_read_data(UINT32 fh,BYTE *buf, UINT32 size)
{
	int ret = 0;
#ifdef ENABLE_PE_CACHE
	if(png_cache_id >= 0)
	{
		ret = pe_cache_read(png_cache_id, buf, size, 0xFFFFFFFF);
		return ret;
	}
#endif	

#ifndef ENABLE_PE_CACHE
	FILE *fp =  (FILE*)fh;
	int byte_read;
	
	if(fp == NULL)
	{
		return -1;
	}

	byte_read = fread(buf, size, 1, fp);

	return ((byte_read < 0) ? 0 : byte_read) ;
#endif
	
}

static int png_seek_data(UINT32 fh,long offset, UINT32 origin)
{
#ifdef ENABLE_PE_CACHE
    if(png_cache_id >= 0)
    {
        pe_cache_seek(png_cache_id, (off_t)offset, origin);
        return 0;
    }
#endif

#ifndef ENABLE_PE_CACHE
	FILE *fp = (FILE*)fh;

	if(fp == NULL)
	{
		return -1;
	}

	fseek(fp, (off_t)offset, origin);
#endif
	return 0;
}

static int png_tell_pos(UINT32 fh)
{
 	return 0;
}

static UINT32 jpeg_process(void *value)
{
	unsigned long process = *(unsigned long*)value;
	unsigned long err = process >> 24;
	
	process = (process * 100) >> 16;

	if(process > 100) process = 100;
	
	osal_mutex_lock(jpeg_file.lock, TMO_FEVR);
	if(jpeg_file.mp_cb)
	{
		if(err == 0)
			jpeg_file.mp_cb(MP_IMAGE_DECODE_PROCESS, process);
		else
			jpeg_file.mp_cb(MP_IMAGE_DECODER_ERROR, err);
	}
	osal_mutex_unlock(jpeg_file.lock);

	return 0;
}

static void pic_png_init(void)
{
	//struct png_cfg cfg;	
	png = png_open(&png_cfg);    
}

static int png_initial(struct pe_image_cfg *pe_image_cfg)
{

#ifdef ENABLE_PE_CACHE	
	png_cache_buffer = (unsigned char *)(((unsigned long)pe_image_cfg->decoder_buf) & 0xfffffff0);
	png_cache_buffer = (unsigned char *)(((unsigned long)png_cache_buffer & 0x0fffffff) | 0x80000000);
	
#endif
    MEMSET((void *)&png_cfg,0,sizeof(struct png_cfg));
	png_cfg.bit_buf_start = pe_image_cfg->decoder_buf +PNG_CACHE_SIZE;
	png_cfg.bit_buf_size = PNG_BIT_SIZE;
	png_cfg.dec_buf_start = png_cfg.bit_buf_start + PNG_BIT_SIZE;
	png_cfg.dec_buf_size = PNG_DEC_SIZE;	
	
}
	
static int png_is_our_file(char *filename)
{
	char *ext;
#ifndef	 _USE_32M_MEM_	
	ext = strrchr(filename, '.');
	if (ext)
	{
		if((!strncasecmp(ext, ".png", 4)))
		{
			return TRUE;
		}
	}
#endif
	return FALSE;
}

#ifdef  DUAL_ENABLE
static int osd_output_init(HANDLE hDev,UINT8 region_id, struct OSDRect *pRect, struct OSDPara *param)
{    
    //UINT32 region_id =0;
    UINT32 trans_color;
    struct OSDRect region_rect;
    struct OSDPara OpenPara;
    
    region_rect.uLeft = pRect->uLeft;
	region_rect.uTop = pRect->uTop;
	region_rect.uWidth = pRect->uWidth;
	region_rect.uHeight = pRect->uHeight;	
    
    OpenPara.eMode = param->eMode;
    OpenPara.uGAlpha = param->uGAlpha;
    OpenPara.uGAlphaEnable = param->uGAlphaEnable;
    OpenPara.uPalletteSel = param->uPalletteSel;
    
    struct osd_device *osd = (struct osd_device *)hDev;
    //OSDDrv_Close((HANDLE)osd);
    OSDDrv_Open((HANDLE)osd, &OpenPara);
    osal_task_sleep(20);
    //trans_color = OSD_GetTransColor(OpenPara.eMode,TRUE);
	OSDDrv_IoCtl((HANDLE)osd, OSD_IO_SET_TRANS_COLOR, 0x00);
	OSDDrv_CreateRegion((HANDLE)osd, region_id, &region_rect, NULL);
    OSDDrv_RegionFill((HANDLE)osd,region_id,&region_rect,0x00);   
    OSDDrv_ShowOnOff((HANDLE)osd, TRUE); 

}
#endif

static PNG_RET png_view_routine_ge(struct png_image *img, struct png_view_par *par)
{
	ge_rect_t dst_rect, src_rect;
	//ge_surface_desc_t *src_surf, *dst_surf;
	struct ge_device *ge_dev;
	INT32 ret;
    ge_operating_entity entity;
    UINT32 layer_id = 0;
    UINT32 region_id =0;
    UINT32 cmd_hdl = 0;

#ifdef  DUAL_ENABLE
    struct osd_device *osd_dev;
    struct osd_device *osd;	
   	struct OSDRect region_rect;
   	struct OSDRect rect;
   	struct OSDRect regionrect;
    struct OSDRect pRect;
   	struct OSDPara OpenPara;
	VSCR source;

    MEMSET((void *)&source,0,sizeof(VSCR));
    
    source.vR.uLeft = par->pos.x;
	source.vR.uTop = par->pos.y;
	source.vR.uWidth = img->size.w;
	source.vR.uHeight = img->size.h;
	source.lpbScr = (UINT8 *)img->graphic_buf;

    rect.uLeft = par->pos.x;
	rect.uTop = par->pos.y;
	rect.uWidth =img->size.w;
	rect.uHeight = img->size.h;
    
   	osd = (struct osd_device *)dev_get_by_id(HLD_DEV_TYPE_OSD, 0);
    if (osd == NULL)
	{
		AD_PNG_PRINTF("%s() osd device find failed!\n", __FUNCTION__);
		return -1;
	}
    
    //OSDDrv_GetRegionPos((HANDLE)osd,0,&regionrect);
    //regionrect.uLeft = regionrect.uTop = 0;
    //OSDDrv_RegionFill((HANDLE)osd,0,&regionrect,0xff);
    
    osd_dev = (struct osd_device *)dev_get_by_id(HLD_DEV_TYPE_OSD, 0);
    if (osd_dev == NULL)
	{
		AD_PNG_PRINTF("%s() osd device find failed!\n", __FUNCTION__);
		return -1;
	}

    else
    {
        OSDDrv_Close((HANDLE)osd_dev);
        AD_PNG_PRINTF("%s() OSD_layer2 is closed!\n", __FUNCTION__);
    }

    pRect.uLeft = 0;
	pRect.uTop = 0;
	pRect.uWidth = 300;//OSD_MAX_WIDTH;//608;
	pRect.uHeight = 300;//OSD_MAX_HEIGHT;//430;	
    
    OpenPara.eMode = OSD_HD_ARGB8888;
    OpenPara.uGAlpha = 0x7f;
    OpenPara.uGAlphaEnable = 0;
    OpenPara.uPalletteSel = 0;
    osd_dev = (struct osd_device *)dev_get_by_id(HLD_DEV_TYPE_OSD, 0);

    
    osd_output_init(osd_dev, region_id, &pRect, &OpenPara);
	ret = OSDDrv_RegionWrite((HANDLE)osd_dev,region_id,&source,&rect);
    
	return ret;
    
#endif

#ifndef  DUAL_ENABLE
	ge_dev = (struct ge_device *)dev_get_by_id(HLD_DEV_TYPE_GE, 0);
    ge_cmd_list_hdl cmd_list = ge_cmd_list_create(ge_dev, 10);
	if (ge_dev == NULL)
	{
		AD_PNG_PRINTF("%s() ge device find failed!\n", __FUNCTION__);
		return -1;
	}

	//src_surf = (ge_surface_desc_t *)ad_pic_get_ge_surface(AD_SRC_SURFACE);
	//dst_surf = (ge_surface_desc_t *)ad_pic_get_ge_surface(AD_DST_SURFACE);

	//if (src_surf == NULL || dst_surf == NULL)
	//	return -1;
	
	src_rect.left = 0;
	src_rect.top = 0;
	src_rect.width = img->size.w;
	src_rect.height = img->size.h;
	
	dst_rect.left = par->pos.x;
	dst_rect.top = par->pos.y;
	dst_rect.width = src_rect.width;
	dst_rect.height = src_rect.height;

    ge_cmd_list_new(ge_dev,cmd_list,GE_COMPILE_AND_EXECUTE);
    ge_gma_set_region_to_cmd_list(ge_dev,layer_id,region_id,cmd_list);

    entity.color_format = GE_PF_ARGB8888;
	entity.base_address = (UINT8 *)img->graphic_buf;
	entity.data_decoder = GE_DECODER_DISABLE;
	entity.pixel_pitch = src_rect.width;
    entity.modify_flags = GE_BA_FLAG_ADDR|GE_BA_FLAG_FORMAT|GE_BA_FLAG_PITCH;

    cmd_hdl = ge_cmd_begin(ge_dev,cmd_list,GE_DRAW_BITMAP);				
	ge_set_operating_entity(ge_dev,cmd_hdl,GE_PTN,&entity);	
	ge_set_clut_rgb_order(ge_dev,cmd_hdl,GE_RGB_ORDER_ARGB);
    //ge_set_rgb_expansion(ge_dev,cmd_hdl,GE_SRC, GE_RGB_EXPAN_MSB_TO_LSB);
	ge_set_xy(ge_dev,cmd_hdl,GE_DST,dst_rect.left,dst_rect.top);
	ge_set_xy(ge_dev,cmd_hdl,GE_SRC,dst_rect.left,dst_rect.top);
	ge_set_xy(ge_dev,cmd_hdl,GE_PTN,src_rect.left,src_rect.top);                
	ge_set_wh(ge_dev,cmd_hdl,GE_DST_PTN,dst_rect.width,dst_rect.height);

	ge_cmd_end(ge_dev,cmd_hdl);
	
	return ret;
#endif
}

//struct Image_Display_Par *pimage_diplay = (struct Image_Display_Par *)para2; // add for image display from YUV

enum Output_Frame_Ret_Code imagedec_mp_request(void *dev, void *pinfo);
BOOL imagedec_mp_release(void *dev,UINT8 utop_idx,UINT8 frm_array_idx);

enum Output_Frame_Ret_Code imagedec_mp_request_ext(void *dev,struct Display_Info *pinfo,struct Request_Info *pRequest_Info);
RET_CODE imagedec_mp_release_ext(void *pdev,struct Release_Info *pRelease_Info);//UINT8 utop_idx,UINT8 frm_array_idx)


static void pic_png_dec(png_file fh, struct OSDRect *rect)
{
	struct png_file file;
	MEMSET((void *)&file, 0, sizeof(struct png_file));
	file.handle = fh;
	file.fread = png_read_data;
	file.fseek = png_seek_data;
	file.ftell = png_tell_pos;

	struct png_image img;
	MEMSET((void *)&img, 0, sizeof(struct png_image));
#ifdef PNG_OUT_CLUT8_PIC
	img.cf_defined = PNG_CF_DEFINED_AUTOMATICALLY;
#else
	img.cf_defined = PNG_CF_DEFINED_MANUALLY; 
#endif
	img.plt_type = PNG_PLT_AYCBCR8888;

	png_dec(png, &file, &img, PNG_SYNC);  //the synchronous mode

	struct png_view_par view_par;
	MEMSET((void *)&view_par, 0, sizeof(struct png_view_par));
	view_par.handle0 = 0;
	view_par.pos.x = rect->uLeft;
	view_par.pos.y = rect->uTop;
	png_view(png, &img, &view_par, png_view_routine_ge);
}

static int png_decode(char *filename, void *args)
{
	png_file fh;
	struct OSDRect rect;

#ifndef ENABLE_PE_CACHE
	FILE *fp = fopen(filename, "rb");
	if(fp == NULL)
	{
		return -1;
	}
	fh = (gif_file)fp;
#endif

/*
#ifdef ENABLE_PE_CACHE
	if(gif_cache_id >= 0) pe_cache_close(gif_cache_id);
	gif_cache_id = -1;
#endif
*/

#ifdef ENABLE_PE_CACHE
	AD_PNG_PRINTF("filename: %s\n", filename);
	png_cache_id = pe_cache_open(filename, png_cache_buffer, PNG_CACHE_SIZE, PNG_BLOCK_SIZE);
	AD_PNG_PRINTF("filename: %s,  pe_cache_id: %d\n", filename, png_cache_id);
	if (png_cache_id < 0)
		return -1;
#endif

#ifdef ENABLE_PE_CACHE
	fh = 1;			// avoid imagedec_decode return FALSE without FS API
#endif

	rect.uLeft = 100;
	rect.uTop = 40;
	rect.uHeight = 600;
	rect.uWidth = 800;
	
	pic_png_init();
	pic_png_dec(fh, &rect);

/*
#ifdef ENABLE_PE_CACHE
	pe_cache_close(png_cache_id);
	png_cache_id = -1;
#endif

#ifndef ENABLE_PE_CACHE
	fclose(fp);
	fp = NULL;
#endif
*/
	
	
	return SUCCESS;	
}

static int png_abort(void)
{
    struct osd_device *osd_dev;
    struct OSDRect rect;
	/*
	if(jpeg_file.id > 0)
	{
		osal_mutex_lock(jpeg_file.lock, TMO_FEVR);
		jpeg_file.mp_cb = NULL;
		osal_mutex_unlock(jpeg_file.lock);

		imagedec_stop(jpeg_file.id);

		while(jpeg_file.task_terminated == 1)
		{
			osal_task_sleep(10);
		}
	}
	*/
#ifdef ENABLE_PE_CACHE
	if(png_cache_id >= 0) pe_cache_close(png_cache_id);
	png_cache_id = -1;
#endif

	if (png == NULL)
		return;
/*	png_stop(png);
*/	png_close(png);
#ifndef  DUAL_ENABLE
    osd_dev = (struct osd_device *)dev_get_by_id(HLD_DEV_TYPE_OSD, 0);
    OSDDrv_GetRegionPos((HANDLE)osd_dev,0,&rect);
    rect.uLeft = rect.uTop = 0;
    OSDDrv_RegionFill((HANDLE)osd_dev,0,&rect,0x00);
#endif
    //OSD_ClearScreen();
#ifdef  DUAL_ENABLE
    osd_dev = (struct osd_device *)dev_get_by_id(HLD_DEV_TYPE_OSD, 0); 
    OSDDrv_ShowOnOff((HANDLE)osd_dev,FALSE);
    OSDDrv_Close((HANDLE)osd_dev);  
   
#endif
	png = NULL;
	
}


ImagePlugin png_plugin =
{
	.handle = 0,
	.filename = NULL,
	.description = NULL,
	.init = png_initial,
	.about = NULL,
	.configure = NULL,
	.is_our_file = png_is_our_file,
	.decode = png_decode,
	.show = NULL,
	.abort = png_abort,
	.cleanup = NULL,
	.get_info = NULL,
	.rotate = NULL,
	.zoom = NULL,
};

